一、如何启用UART并建立通信连接
1.1 UART建立通信连接流程
如何使用 UART 驱动程序的函数和数据类型在 ESP32-S3 和其他 UART 设备之间建立通信。基本编程流程分为以下几个步骤:
- 安装驱动程序 - 为 UART 驱动程序分配 ESP32-S3 资源
- 设置通信参数 - 设置波特率、数据位、停止位等
- 设置通信管脚 - 分配连接设备的管脚
- 运行 UART 通信 - 发送/接收数据
- 使用中断 - 触发特定通信事件的中断
- 删除驱动程序 - 如无需 UART 通信,则释放已分配的资源
步骤 1 到 3 为配置阶段,步骤 4 为 UART 运行阶段,步骤 5 和 6 为可选步骤。
UART 驱动程序函数通过 uart_port_t 识别不同的 UART 控制器。调用以下所有函数均需此标识。
1.2 配置则按以下步骤安装驱动、配置uart和绑定引脚
| 步骤 | 函数 | 作用 | 类比 |
|---|---|---|---|
| 1️⃣ 安装驱动 | uart_driver_install() | 分配内存、初始化 FIFO、注册中断、创建缓冲区 | “给 UART 装操作系统、分配硬件资源” |
| 2️⃣ 配置参数 | uart_param_config() | 设置波特率、数据位、时钟源等通信规则 | “结构体传参,设置通信语言(如普通话/英语)” |
| 3️⃣ 绑定引脚 | uart_set_pin() | 将 UART 的 TX/RX 映射到具体 GPIO | “插上网线/接上麦克风” |
CAUTION
✅ 顺序不能乱:先装驱动 → 再设规则 → 最后接线



1.2.1 第一步:uart_driver_install() —— 安装驱动(激活硬件 + 分配资源)
c
esp_err_t uart_driver_install(
uart_port_t uart_num, // UART 端口号(0, 1, 2)
int rx_buffer_size, // RX 环形缓冲区大小(字节)
int tx_buffer_size, // TX 环形缓冲区大小(字节)
int queue_size, // 事件队列深度
QueueHandle_t* uart_queue, // 事件队列句柄(输出参数)
int intr_alloc_flags // 中断分配标志
);参数详解:
| 参数 | 你的值 | 含义 | 为什么这么设? |
|---|---|---|---|
uart_num | UART_NUM_0 | 使用哪个 UART 控制器 | ESP32-S3 有 3 个 UART(0/1/2) |
rx_buffer_size | RX_BUF_SIZE * 2 = 2048 | 分配多大的内存缓存接收数据 | 防止突发数据丢失,越大越安全(但占 RAM) |
tx_buffer_size | 0 | TX 是否用环形缓冲区 | 0= 同步发送(uart_write_bytes会阻塞直到发完), 适合小数据 |
queue_size | 0 | 是否使用事件队列 | 0= 不用,你用轮询方式读取(uart_read_bytes) |
uart_queue | NULL | 不需要事件队列句柄 | 因为queue_size=0 |
intr_alloc_flags | 0 | 中断分配选项 | 默认即可,除非你需要高优先级中断 |
⚙️ 内部做了什么?
- 使能 UART 模块的时钟(APB_CLK)
- 复位 UART 硬件 FIFO
- 分配
rx_buffer_size字节的 Ring Buffer(在内部 RAM) - 注册 UART 中断服务程序(ISR)
- 初始化 FreeRTOS 队列(如果
queue_size > 0)
💡 关键点:
这个函数必须最先调用!
因为后续的uart_param_config()和uart_set_pin()都要操作硬件寄存器,而硬件必须先被“激活”。
1.2.2 第二步:uart_param_config() —— 配置通信规则(UART通信参数配置)
1.2.2.1 启用UART前,也就是uart_param_config()传参前必须先配置结构体uart_config_t
esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config)
c
uart_config_t my_uart_config = {0};
my_uart_config.baud_rate = 115200;
my_uart_config.data_bits = UART_DATA_8_BITS;
my_uart_config.stop_bits = UART_STOP_BITS_1;
my_uart_config.parity = UART_PARITY_DISABLE;
my_uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
my_uart_config.source_clk = UART_SCLK_DEFAULT;
// 或者UART_SCLK_APB都可以,因为ESP-IDF中UART的时钟源默认就是APB_CLK时钟源
// 因为有且只有UART_SCLK_APB\UART_SCLK_RTC\UART_SCLK_XTAL这三个选项可以选,选default就是选APB_CLK
// .flags 通常不需要配置,绝大多数应用(包括你的 UART 回显、调试通信等)都可以完全忽略它。
// 示例项目不配置它,是因为它的功能属于 高级电源管理场景,普通用户用不到。c
esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t* config);作用:
将你定义的 uart_config_t 结构体写入 UART 硬件寄存器。
内部关键操作:
- 选择时钟源(通过
UART_SCLK_SEL寄存器)- 你选了
UART_SCLK_DEFAULT→ 实际是APB_CLK = 80MHz
- 你选了
- 计算波特率分频系数
- 公式:
div = APB_CLK / baud_rate - 例如:80,000,000 / 115200 ≈ 694.44 → 写入
UART_CLKDIV_REG
- 公式:
- 配置帧格式
- 数据位(8 bits)→
UART_BIT_NUM - 停止位(1 bit)→
UART_STOP_BIT_NUM - 奇偶校验(无)→
UART_PARITY_EN = 0
- 数据位(8 bits)→
✅ 为什么在这一步做?
因为硬件必须先被uart_driver_install()激活,才能写寄存器!
1.2.3 第三步:uart_set_pin() —— 绑定物理引脚(连接物理线路)
c
esp_err_t uart_set_pin(
uart_port_t uart_num,
int tx_io_num, // TX 引脚(如 GPIO43)
int rx_io_num, // RX 引脚(如 GPIO44)
int rts_io_num, // RTS 引脚(硬件流控,不用则 UART_PIN_NO_CHANGE)
int cts_io_num // CTS 引脚(硬件流控,不用则 UART_PIN_NO_CHANGE)
);作用:
- 调用 GPIO 矩阵(GPIO Matrix) 功能
- 将 UART 内部信号(如
U0TXD)路由到指定 GPIO - 配置 GPIO 为 复用功能模式(Alternate Function)
为什么需要这一步?
- ESP32-S3 的 UART 信号 不固定在某个 GPIO
- 你可以把 UART0 TX 放在 任意 GPIO(只要支持)
- 这是通过 I/O MUX 和 GPIO Matrix 实现的
✅ 示例:
uart_set_pin(UART_NUM_0, 43, 44, ...)
→ 把 UART0 的 TX 信号连到 GPIO43,RX 连到 GPIO44
1.2.4 三个函数的核心作用
| 函数 | 核心任务 | 必须? | 类比 |
|---|---|---|---|
uart_driver_install() | 激活硬件 + 分配内存 + 注册中断 | ✅ 必须第一个调用 | “启动引擎” |
uart_param_config() | 设置波特率、数据格式、时钟源 | ✅ 必须 | “设定档位和油门” |
uart_set_pin() | 将 UART 信号连接到物理 GPIO | ✅ 必须 | “挂上变速箱” |
二、借鉴ESP-IDF乐鑫开发示例程序
三、ESP32 UART如何接收数据(uart_read_bytes)?
int uart_read_bytes(uart_port_t uart_num, void *buf, uint32_t length, uint32_t ticks_to_wait)
UART read bytes from UART buffer.
参数:
- uart_num -- UART port number, the max port number is (UART_NUM_MAX -1).
- buf-- pointer to the buffer.
- length -- data length
- ticks_to_wait -- sTimeout, count in RTOS ticks
返回:
- (-1) Error
- OTHERS (>=0) The number of bytes read from UART buffer
3.1 官方示例代码
plain
static void rx_task(void *arg)
{
static const char *RX_TASK_TAG = "RX_TASK";
esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE + 1);
while (1) {
const int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE, 1000 / portTICK_PERIOD_MS);
if (rxBytes > 0) {
data[rxBytes] = 0;
ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
}
}
free(data);
}3.2 官方技术开发文档参考代码
plain
// Read data from UART.
const uart_port_t uart_num = UART_NUM_2;
uint8_t data[128];
int length = 0;
ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));
length = uart_read_bytes(uart_num, data, length, 100);